// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/mojo_bindings_controller.h"

#include "content/common/view_messages.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h"
#include "content/renderer/mojo_context_state.h"
#include "gin/per_context_data.h"
#include "third_party/WebKit/public/web/WebKit.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "v8/include/v8.h"

namespace content {

namespace {

    const char kMojoContextStateKey[] = "MojoContextState";

    struct MojoContextStateData : public base::SupportsUserData::Data {
        std::unique_ptr<MojoContextState> state;
    };

} // namespace

MojoBindingsController::MojoBindingsController(RenderFrame* render_frame,
    MojoBindingsType bindings_type)
    : RenderFrameObserver(render_frame)
    , RenderFrameObserverTracker<MojoBindingsController>(render_frame)
    , bindings_type_(bindings_type)
{
}

MojoBindingsController::~MojoBindingsController()
{
}

void MojoBindingsController::CreateContextState()
{
    v8::HandleScope handle_scope(blink::mainThreadIsolate());
    blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
    v8::Local<v8::Context> context = frame->mainWorldScriptContext();
    gin::PerContextData* context_data = gin::PerContextData::From(context);
    MojoContextStateData* data = new MojoContextStateData;
    data->state.reset(new MojoContextState(frame, context, bindings_type_));
    context_data->SetUserData(kMojoContextStateKey, data);
}

void MojoBindingsController::DestroyContextState(
    v8::Local<v8::Context> context)
{
    gin::PerContextData* context_data = gin::PerContextData::From(context);
    if (!context_data)
        return;
    context_data->RemoveUserData(kMojoContextStateKey);
}

MojoContextState* MojoBindingsController::GetContextState()
{
    v8::HandleScope handle_scope(blink::mainThreadIsolate());
    v8::Local<v8::Context> context = render_frame()->GetWebFrame()->mainWorldScriptContext();
    gin::PerContextData* context_data = gin::PerContextData::From(context);
    if (!context_data)
        return NULL;
    MojoContextStateData* context_state = static_cast<MojoContextStateData*>(
        context_data->GetUserData(kMojoContextStateKey));
    return context_state ? context_state->state.get() : NULL;
}

void MojoBindingsController::WillReleaseScriptContext(
    v8::Local<v8::Context> context,
    int world_id)
{
    DestroyContextState(context);
}

void MojoBindingsController::RunScriptsAtDocumentStart()
{
    CreateContextState();
}

void MojoBindingsController::RunScriptsAtDocumentReady()
{
    v8::HandleScope handle_scope(blink::mainThreadIsolate());
    MojoContextState* state = GetContextState();
    if (state)
        state->Run();
}

void MojoBindingsController::DidClearWindowObject()
{
    // NOTE: this function may be called early on twice. From the constructor
    // mainWorldScriptContext() may trigger this to be called. If we are created
    // before the page is loaded (which is very likely), then on first load this
    // is called. In the case of the latter we may have already supplied the
    // handle to the context state so that if we destroy now the handle is
    // lost. If this is the result of the first load then the contextstate should
    // be empty and we don't need to destroy it.
    MojoContextState* state = GetContextState();
    if (state && !state->module_added())
        return;

    v8::HandleScope handle_scope(blink::mainThreadIsolate());
    DestroyContextState(render_frame()->GetWebFrame()->mainWorldScriptContext());
}

void MojoBindingsController::OnDestruct()
{
    delete this;
}

} // namespace content
